using System;
using System.Security;
using System.Security.Permissions;
namespace Net35.Xrm.Sdk.Client
{
	[SecuritySafeCritical]
	[SecurityPermission(SecurityAction.Demand, Unrestricted = true)]
	public class ServiceChannel<TChannel> : IDisposable where TChannel : class
	{
		private readonly object _lockObject = new object();
		private TChannel _channel;
		private TimeSpan _timeout = ServiceDefaults.DefaultTimeout;
		private bool _updateTimeout = true;
		private bool _disposed;
		public event EventHandler<ChannelFaultedEventArgs> ChannelFaulted;
		public event EventHandler<ChannelEventArgs> ChannelOpened;
		public event EventHandler<ChannelEventArgs> ChannelClosed;
		public TChannel Channel
		{
			get
			{
				if (this._channel == null || this._disposed || !ServiceChannel<TChannel>.IsCommunicationObjectValid(this.CommunicationObject))
				{
					ClientExceptionHelper.ThrowIfNull(this.Factory, "Factory");
					this.ConfigureNewChannel();
				}
				lock (this._lockObject)
				{
					if (this._updateTimeout)
					{
						System.ServiceModel.IContextChannel contextChannel = this._channel as System.ServiceModel.IContextChannel;
						contextChannel.OperationTimeout = this._timeout;
						this._updateTimeout = false;
					}
				}
				return this._channel;
			}
		}
		protected System.ServiceModel.ChannelFactory<TChannel> Factory
		{
			get;
			private set;
		}
		protected System.ServiceModel.ICommunicationObject CommunicationObject
		{
			get
			{
				return this._channel as System.ServiceModel.ICommunicationObject;
			}
		}
		internal TimeSpan Timeout
		{
			get
			{
				return this._timeout;
			}
			set
			{
				this._timeout = value;
				this._updateTimeout = true;
			}
		}
		internal bool IsChannelInvalid
		{
			get
			{
				return this._disposed || this.Factory == null;
			}
		}
		public ServiceChannel(System.ServiceModel.ChannelFactory<TChannel> factory)
		{
			ClientExceptionHelper.ThrowIfNull(factory, "factory");
			this.Factory = factory;
		}
		[SecuritySafeCritical]
		[SecurityPermission(SecurityAction.Demand, Unrestricted = true)]
		protected virtual TChannel CreateChannel()
		{
			return this.Factory.CreateChannel();
		}
		internal static bool IsCommunicationObjectValid(System.ServiceModel.ICommunicationObject communicationObject)
		{
			if (communicationObject != null)
			{
				System.ServiceModel.CommunicationState state = communicationObject.State;
				if (state == System.ServiceModel.CommunicationState.Opened || state == System.ServiceModel.CommunicationState.Opening || state == System.ServiceModel.CommunicationState.Created)
				{
					return true;
				}
			}
			return false;
		}
		private void ConfigureNewChannel()
		{
			this._channel = this.CreateChannel();
			this.CommunicationObject.Opened += new EventHandler(this.Channel_Opened);
			this.CommunicationObject.Faulted += new EventHandler(this.Channel_Faulted);
			this.CommunicationObject.Closed += new EventHandler(this.Channel_Closed);
		}
		private void RemoveChannelEvents()
		{
			if (this.CommunicationObject != null)
			{
				this.CommunicationObject.Opened -= new EventHandler(this.Channel_Opened);
				this.CommunicationObject.Faulted -= new EventHandler(this.Channel_Faulted);
				this.CommunicationObject.Closed -= new EventHandler(this.Channel_Closed);
			}
		}
		public void Open()
		{
			if (this.CommunicationObject != null && this.CommunicationObject.State == System.ServiceModel.CommunicationState.Created)
			{
				try
				{
					this.CommunicationObject.Open();
				}
				catch (Exception exception)
				{
					ChannelFaultedEventArgs channelFaultedEventArgs = new ChannelFaultedEventArgs("Exception when opening an SDK channel", exception);
					this.OnChannelFaulted(channelFaultedEventArgs);
					if (!channelFaultedEventArgs.Cancel)
					{
						throw;
					}
				}
			}
		}
		public void Abort()
		{
			if (this.CommunicationObject != null)
			{
				this.CommunicationObject.Abort();
			}
		}
		public void Close()
		{
			if (this.CommunicationObject != null)
			{
				this.RemoveChannelEvents();
				this.CommunicationObject.Close(true);
			}
		}
		private void Channel_Faulted(object sender, EventArgs e)
		{
			System.ServiceModel.ICommunicationObject communicationObject = this._channel as System.ServiceModel.ICommunicationObject;
			this._channel = default(TChannel);
			this.OnChannelFaulted(new ChannelFaultedEventArgs("The channel has entered a faulted state.", null));
			if (communicationObject != null)
			{
				this.RemoveChannelEvents();
				communicationObject.Close(true);
			}
		}
		private void Channel_Closed(object sender, EventArgs e)
		{
			this.OnChannelClosed(new ChannelEventArgs("The channel has entered a closed state."));
		}
		private void Channel_Opened(object sender, EventArgs e)
		{
			this.OnChannelOpened(new ChannelEventArgs("The channel has entered an opened state."));
		}
		protected virtual void OnChannelFaulted(ChannelFaultedEventArgs args)
		{
			if (this.ChannelFaulted != null)
			{
				this.ChannelFaulted(this, args);
			}
		}
		protected virtual void OnChannelClosed(ChannelEventArgs args)
		{
			if (this.ChannelClosed != null)
			{
				this.ChannelClosed(this, args);
			}
		}
		protected virtual void OnChannelOpened(ChannelEventArgs args)
		{
			if (this.ChannelOpened != null)
			{
				this.ChannelOpened(this, args);
			}
		}
		public void Dispose()
		{
			this.Dispose(true);
			GC.SuppressFinalize(this);
		}
		~ServiceChannel()
		{
			this.Dispose(false);
		}
		private void Dispose(bool disposing)
		{
			if (this._disposed)
			{
				return;
			}
			if (this._channel != null)
			{
				try
				{
					this.Close();
				}
				finally
				{
					this._channel = default(TChannel);
				}
			}
			this.Factory = null;
			if (disposing)
			{
				this._disposed = true;
			}
		}
	}
}
